home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / gnuish / swalibas / swap.c < prev    next >
C/C++ Source or Header  |  1990-09-10  |  31KB  |  1,226 lines

  1. /*  swap.c - swap parent to disk, EMS, or XMS while executing child (MS-DOS)
  2.     Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
  3.  
  4.     This program is free software; you can redistribute it and/or modify
  5.     it under the terms of the GNU General Public License as published by
  6.     the Free Software Foundation; either version 1, or (at your option)
  7.     any later version.
  8.  
  9.     This program is distributed in the hope that it will be useful,
  10.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.     GNU General Public License for more details.
  13.  
  14.     You should have received a copy of the GNU General Public License
  15.     along with this program; if not, write to the Free Software
  16.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. static char RCS_id[] =
  19. "$Header: e:/gnu/swaplib/RCS/swap.c'v 0.9 90/09/09 21:43:50 tho Stable $";
  20.  
  21. /* Please note the following naming convention:
  22.  
  23.       *    functions and variables beginning with "_swap_kernel_" do *not*
  24.     refer to functions and variables in the text and data segments
  25.     which are swapped out.   */
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31. #include <errno.h>
  32. #include <signal.h>
  33. #include <sys/types.h>
  34. #include "swaplib.h"
  35. #include <dos.h>
  36.  
  37. /* Macros for dealing with Intel "huge" pointers.  */
  38.  
  39. /* Add OFF to PTR, taking care of segment overflow  */
  40.  
  41. #define FP_ADD(ptr,off) \
  42.   (FP_SEG (ptr) += (off) >> 4, FP_OFF (ptr) += (off) & 0xff, ptr)
  43.  
  44. /* Paragraph-align PTR.  */
  45.  
  46. #define FP_PARA_ALIGN(ptr)            \
  47.   {                        \
  48.     FP_SEG (ptr)                \
  49.       += ((FP_OFF (ptr) + 15) >> 4) + 1;    \
  50.     FP_OFF (ptr) = 0x0000;            \
  51.   }
  52.  
  53.  
  54. /* don't call MS' stack checker (it will fail with the local stack!)  */
  55.  
  56. #pragma check_stack (off)
  57.  
  58.  
  59. #define FILE_IO_BLKSIZE        0x8000
  60. #define MAX_MSDOS_CMDLINE    126
  61. #define MAX_MSDOS_PATH        144
  62. #define MAX_MSDOS_MCBS        25
  63.  
  64.  
  65. /* Atribute to force storage in the code segment! */
  66. #define CODE _based (_segname ("_CODE"))
  67.  
  68. /* The size of the code and data swapped out in bytes.  */
  69. static off_t CODE _swap_kernel_swapped_bytes;
  70.  
  71. /* A XMS, EMS or disk handle for the swapped program.  */
  72. static unsigned int CODE _swap_kernel_handle;
  73.  
  74. /* A safe copy of _PSP.  */
  75. static unsigned int CODE _swap_kernel_psp;
  76.  
  77. /* The size of the swap kernel in paragraphs (16b).  */
  78. static unsigned int CODE _swap_kernel_resident_paras;
  79.  
  80. /* The size of the first block (which contains the kernel). */
  81. static unsigned int CODE _swap_kernel_first_block_paras;
  82.  
  83. /* The environment actually passed to the child (starts right
  84.    after  the kernel).  */
  85. static char _far * CODE _swap_kernel_environment;
  86.  
  87. /* The environment prepared for the child (will be copied
  88.    to _SWAP_KERNEL_ENVIRONMENT).  */
  89. static char _far * CODE _swap_kernel_environment_ptr;
  90.  
  91. #ifdef USE_EMS
  92. /* Pointer to the EMS pageframe.  */
  93. static char _far * CODE _swap_kernel_ems_page_frame;
  94. #endif
  95.  
  96.  
  97. /* Parameters for DOS for creating a child process */
  98. #pragma pack (1)
  99. static struct
  100. {
  101.   _segment environment_segment;
  102.   char _far *cmd_line_ptr;
  103.   char _far *fcb_ptr_1;
  104.   char _far *fcb_ptr_2;
  105. } CODE _swap_kernel_parameter_block;
  106. #pragma pack ()
  107.  
  108. static char CODE _swap_kernel_path[MAX_MSDOS_PATH];
  109. static char CODE _swap_kernel_cmdline[MAX_MSDOS_CMDLINE + 2];
  110. static char CODE _swap_kernel_fcb_1[16];    /* FCBs for DOS.  */
  111. static char CODE _swap_kernel_fcb_2[16];
  112. static unsigned int CODE _swap_kernel_environ_seg;
  113. static unsigned int CODE _swap_kernel_environment_size;
  114. static int CODE _swap_kernel_return_code;
  115.  
  116. static struct
  117. {
  118.   _segment loc;
  119.   unsigned int len;
  120. } CODE _swap_kernel_orig_mcbs[MAX_MSDOS_MCBS];
  121.  
  122. static int (*CODE _swap_kernel_swap_in) (int handle, void _far * buffer,
  123.                      long bytes);
  124. static int (*CODE _swap_kernel_swap_out) (int handle, void _far * buffer,
  125.                       long bytes);
  126.  
  127. #ifdef USE_XMS
  128. static char _far * CODE _swap_kernel_xms_control;
  129.  
  130. #pragma pack (1)
  131. static struct
  132. {
  133.   long length;
  134.   int src_handle;
  135.   long src_offset;
  136.   int dest_handle;
  137.   long dest_offset;
  138. } CODE _swap_kernel_xms_move_table;
  139.  
  140. #pragma pack ()
  141. #endif /* USE_XMS */
  142.  
  143.  
  144. /* The local stack */
  145.  
  146. #define STACK_SIZE    0x200
  147.  
  148. static char CODE _swap_kernel_local_stack[STACK_SIZE];
  149. static unsigned int CODE _swap_kernel_stack_pointer;
  150. static _segment CODE _swap_kernel_stack_segment;
  151.  
  152. static void _swap_kernel_end (void);
  153.  
  154.  
  155. /* "Save" version of memcpy.  */
  156. static void _far * _swap_kernel_memcpy (void _far *destination,
  157.                     const void _far *source, size_t bytes);
  158.  
  159. /* MS-DOS interface */
  160.  
  161. #pragma pack (1)        /* won't fit, if we don't pack! */
  162. struct mcb_info
  163. {
  164.   char         id_byte;    /* 'M': not, 'Z': last MCB    */
  165.   unsigned short owner;        /* PSP of owner            */
  166.   unsigned short length;    /* length (in paragraphs = 16b)    */
  167. };
  168. #pragma pack ()
  169.  
  170. static void _swap_kernel_fatal_error (char code, char CODE *msg, size_t len);
  171. static int  _swap_kernel_free_block (_segment block);
  172. static int  _swap_kernel_set_block (_segment block, unsigned int paras);
  173. static unsigned int  _swap_kernel_allocate_block (unsigned int paras);
  174. static void _swap_kernel_free_upper_blocks (void);
  175. static void _swap_kernel_reclaim_blocks (void);
  176. static int _swap_kernel_exec (void);
  177.  
  178. /* Disk I/O */
  179.  
  180. static unsigned int _swap_kernel_read (int handle, void _far * buffer,
  181.                        size_t bytes);
  182. static unsigned int _swap_kernel_write (int handle, void _far * buffer,
  183.                     size_t bytes);
  184. static int _swap_kernel_rewind (int handle);
  185. static int _swap_kernel_write_to_handle (int handle, void _far * buffer,
  186.                      long size);
  187. static int _swap_kernel_read_from_handle (int handle, void _far * buffer,
  188.                       long size);
  189.  
  190.  
  191. /* XMS */
  192.  
  193. #ifdef USE_XMS
  194. static int _swap_kernel_xms_move_out (int handle, void _far * buffer,
  195.                       long bytes);
  196. static int _swap_kernel_xms_move_in (int handle, void _far * buffer,
  197.                      long bytes);
  198. static int _swap_kernel_xms_move (void);
  199. static int xms_installed (void);
  200. static void xms_get_control_function (void);
  201. static unsigned int xms_allocate_memory (unsigned int kilobytes);
  202. static unsigned int xms_free_memory (unsigned int handle);
  203. #endif /* USE_XMS */
  204.  
  205. /* EMS */
  206.  
  207. #ifdef USE_EMS
  208. static int _swap_kernel_ems_save_page_map (int handle);
  209. static int _swap_kernel_ems_restore_page_map (int handle);
  210. static int _swap_kernel_ems_map_logical_page (int handle, int logical_page);
  211. static int _swap_kernel_ems_move_out (int handle, void _far *buffer,
  212.                       long bytes);
  213. static int _swap_kernel_ems_move_in (int handle, void _far *buffer,
  214.                      long bytes);
  215. static int ems_present (void);
  216. static int ems_alloc_pages (int n);
  217. static int ems_free_pages (unsigned int handle);
  218. static void _far *ems_get_page_frame (void);
  219. #endif /* USE_EMS */
  220.  
  221. /* Signal handling */
  222.  
  223. typedef void _interrupt _far signal_handler (void);
  224.  
  225. static signal_handler *_swap_kernel_caller_int23;
  226. static void _far _swap_kernel_int23_handler (void);
  227.  
  228. static int CODE _swap_kernel_user_interrupt = 0;     /* record interrupts  */
  229. static char CODE _swap_kernel_int23_message[] = "\r\n\a*** Interrupt.\r\n";
  230.  
  231. /* "Higher" level code.  */
  232.  
  233. static void _swap_kernel_setup_environment (void);
  234. static int  _swap_kernel_spawn_child (void);
  235.  
  236. static void install_parameters (char *path, char *cmdline, char *env,
  237.                 size_t size);
  238. static struct mcb_info far *last_mcb (void);
  239. static int  alloc_swap_kernel_file (char *name, long size);
  240. static unsigned int cleanup_swap_kernel_file (unsigned handle, char *name);
  241.  
  242.  
  243. /* Very Fatal Error messages.  */
  244.  
  245. static char CODE _swap_kernel_err_msg_head[] = \
  246.   "\r\nFatal error in memory management. Aborting.\r\nReason: ";
  247. static char CODE _swap_kernel_err_msg_0[] = "Can't reallocate core.";
  248. static char CODE _swap_kernel_err_msg_1[] = "Can't swap code back.";
  249. static char CODE _swap_kernel_err_msg_2[] = "Can't release core.";
  250. static char CODE _swap_kernel_err_msg_3[] = "Too many MCBs.";
  251.  
  252. #define SWAP_FATAL_ERROR(num)                    \
  253.   _swap_kernel_fatal_error (-1, _swap_kernel_err_msg_##num,    \
  254.                 sizeof (_swap_kernel_err_msg_##num))
  255.  
  256.  
  257. void
  258. _swap_kernel_setup_environment (void)
  259. {
  260.   _swap_kernel_resident_paras
  261.     = FP_SEG (_swap_kernel_environment) - _swap_kernel_psp;
  262.  
  263.   if (_swap_kernel_environment_size && *_swap_kernel_environment_ptr)
  264.     {
  265.       _swap_kernel_memcpy (_swap_kernel_environment,
  266.                _swap_kernel_environment_ptr,
  267.                _swap_kernel_environment_size);
  268.       _swap_kernel_resident_paras += (_swap_kernel_environment_size + 15) >> 4;
  269.       _swap_kernel_environ_seg = FP_SEG (_swap_kernel_environment);
  270.     }
  271.   else
  272.     _swap_kernel_environ_seg = 0;    /* pass our own environment */
  273. }
  274.  
  275. static void _far *
  276. _swap_kernel_memcpy (void _far *destination, const void _far *source,
  277.              size_t bytes)
  278. {
  279.   _asm
  280.     {
  281.       push ds
  282.       mov  cx,bytes
  283.       lds  si,source
  284.       les  di,destination
  285.       shr  cx,1            /* save odd byte in carry flag */
  286.       rep  movsw        /* move words (for speed) */
  287.       adc  cx,cx
  288.       rep  movsb        /* move the odd byte, if any */
  289.       pop  ds
  290.     }
  291.  
  292.   return destination;
  293. }
  294.  
  295.  
  296. /* Memory management.
  297.  
  298.    WARNING:  this used undocumented MS-DOS features.
  299.  
  300.    This features seem to be very stable anyway (Microsoft obviously uses
  301.    them in their own programs and since they won't want to break them,
  302.    these feaatures shouldn't go away.  */
  303.  
  304. /* Does this MCB belong to us?  */
  305. #define OUR_MCB(mcb) ((mcb)->owner == _swap_kernel_psp)
  306.  
  307. /* Return a pointer to OUR first MCB */
  308. #define FIRST_MCB(mcb) \
  309.   (FP_SEG (mcb) = _swap_kernel_psp - 1, FP_OFF (mcb) = 0, mcb)
  310.  
  311. /* Return a pointer to the next MCB */
  312. #define NEXT_MCB(mcb) \
  313.   (FP_SEG (mcb) = FP_SEG (mcb) + mcb->length + 1, mcb)
  314.  
  315. int
  316. _swap_kernel_free_block (_segment block)
  317. {
  318.   _asm
  319.     {
  320.       mov    ax, block;
  321.       mov    es, ax;
  322.       mov    ah, 0x49        /* MS-DOS Free Allocated Memory */
  323.       int    0x21
  324.       jc    failed
  325.       xor    ax, ax;            /* success */
  326.     failed:
  327.     }
  328. }
  329.  
  330. int
  331. _swap_kernel_set_block (_segment block, unsigned int paras)
  332. {
  333.   _asm
  334.     {
  335.       mov    ax, block
  336.       mov    es, ax
  337.       mov    bx, paras
  338.       mov    ah, 4ah            /* MS-DOS Set Block */
  339.       int    0x21
  340.       jc    failed
  341.       xor    ax, ax            /* success */
  342.     failed:
  343.     }
  344. }
  345.  
  346.  
  347. static unsigned int
  348.  _swap_kernel_allocate_block (unsigned int paras)
  349. {
  350.   _asm
  351.     {
  352.       mov    bx, paras
  353.       mov    ah, 0x48    /* MS-DOS Allocate Memory */
  354.       int    0x21
  355.       jnc    done
  356.       mov    ax, 0x0000    /* failed */
  357.     done:
  358.     }
  359. }
  360.  
  361.  
  362. /* Free, one by one, the memoy blocks owned by us.  This excluded the
  363.    first block, which will be shrunk later.  _swap_kernel_orig_mcbs will be
  364.    zero-terminated. */
  365.  
  366. void
  367. _swap_kernel_free_upper_blocks (void)
  368. {
  369.   int i = 0;
  370.   struct mcb_info far *mcb;
  371.  
  372.   FIRST_MCB (mcb);
  373.  
  374.   while (mcb->id_byte == 'M')
  375.     {
  376.       NEXT_MCB (mcb);    /* leave the first block intact (for the moment)  */
  377.  
  378.       if (OUR_MCB (mcb))
  379.     {
  380.       if (i >= MAX_MSDOS_MCBS)
  381.         SWAP_FATAL_ERROR (3);
  382.       if (_swap_kernel_free_block (FP_SEG (mcb) + 1))
  383.         SWAP_FATAL_ERROR (2);
  384.       _swap_kernel_orig_mcbs[i].loc = FP_SEG (mcb) + 1;
  385.       _swap_kernel_orig_mcbs[i].len = mcb->length;
  386.       i++;
  387.     }
  388.     }
  389.   _swap_kernel_orig_mcbs[i].loc = 0x000;
  390. }
  391.  
  392.  
  393. /* Reclaim, one by one, the original memory blocks, as stored in
  394.    _swap_kernel_orig_mcbs.  From the MS-DOS point of view, this should be not
  395.    necessary, since MS-DOS keeps (to my knowledge) no internal record of
  396.    the memory allocation and the original MCBs are restored together with
  397.    the image.  But in this way we can catch the fatal condition when the
  398.    child has (illegally) left a resident grandchild.  Also we will be
  399.    warned if our methos fails with future MS-DOS versions.  */
  400.  
  401. void
  402. _swap_kernel_reclaim_blocks (void)
  403. {
  404.   int i = 0;
  405.  
  406.   while (_swap_kernel_orig_mcbs[i].loc != 0x000)
  407.     if (_swap_kernel_allocate_block (_swap_kernel_orig_mcbs[i].len)
  408.     != _swap_kernel_orig_mcbs[i].loc)
  409.       SWAP_FATAL_ERROR (0);
  410.     else
  411.       i++;
  412. }
  413.  
  414.  
  415. int
  416. _swap_kernel_exec (void)
  417. {
  418.   _swap_kernel_parameter_block.environment_segment
  419.     = _swap_kernel_environ_seg;
  420.   _swap_kernel_parameter_block.cmd_line_ptr
  421.     = (char _far *) &_swap_kernel_cmdline;
  422.   _swap_kernel_parameter_block.fcb_ptr_1
  423.     = (char _far *) &_swap_kernel_fcb_1;
  424.   _swap_kernel_parameter_block.fcb_ptr_2
  425.     = (char _far *) &_swap_kernel_fcb_2;
  426.  
  427.   /* The compiler saves si and di by himself.  */
  428.  
  429.   _asm
  430.     {
  431.       push    ds        /* save ds */
  432.  
  433.       mov    ax, cs        /* let es and ds point into code segment */
  434.       mov    es, ax
  435.       mov    ds, ax
  436.  
  437.       mov    si, offset _swap_kernel_cmdline    /* parse commandline */
  438.       mov    di, offset _swap_kernel_fcb_1        /* create first FCB */
  439.       mov    ax, 0x2901         /* MS-DOS Parse File Name */
  440.       int    0x21
  441.       mov    di, offset _swap_kernel_fcb_2        /* create second FCB */
  442.       mov    ax, 0x2901          /* MS-DOS Parse File Name */
  443.       int    0x21
  444.       mov    bx, offset _swap_kernel_parameter_block /* es:bx */
  445.       mov    dx, offset _swap_kernel_path         /* ds:dx */
  446.  
  447.       mov    ax, 0x4b00        /* MS-DOS Load and Execute Program */
  448.       int    21h
  449.       mov    ax, 0ffffh        /* assume failure */
  450.       jc    failed
  451.  
  452.       mov    ah, 0x4d        /* MS-DOS Get Return Code of Child */
  453.       int    21h
  454.       mov    _swap_kernel_return_code, ax    /* store return code */
  455.  
  456.     failed:
  457.       pop    ds        /* restore ds */
  458.     }
  459. }
  460.  
  461. int
  462. _swap_kernel_spawn_child (void)    /* CAN'T TAKE PARAMETERS! */
  463. {
  464.   /* void */            /* CAN'T HAVE LOCAL VARIABLES!  */
  465.  
  466.     /* FROM HERE ON: DON'T REFER TO THE GLOBAL STACK! */
  467.   _asm
  468.     {
  469.       /* save stack position */
  470.       mov    cs:_swap_kernel_stack_pointer, sp
  471.       mov    cs:_swap_kernel_stack_segment, ss
  472.  
  473.       /* Interrupts off */
  474.       cli
  475.  
  476.       /* Change stack */
  477.       mov    ax, seg _swap_kernel_local_stack
  478.       mov    ss, ax
  479.       mov    sp, offset _swap_kernel_local_stack + STACK_SIZE
  480.  
  481.       /* Interrupts on */
  482.       sti
  483.     }
  484.  
  485.   if ((*_swap_kernel_swap_out) (_swap_kernel_handle, _swap_kernel_environment,
  486.                 _swap_kernel_swapped_bytes))
  487.     return -1;
  488.  
  489.   _swap_kernel_setup_environment ();
  490.   _swap_kernel_free_upper_blocks ();
  491.   _swap_kernel_set_block (_swap_kernel_psp, _swap_kernel_resident_paras);
  492.  
  493.   _swap_kernel_exec ();        /* !!! BIG DEAL !!! */
  494.  
  495.   if (_swap_kernel_set_block (_swap_kernel_psp,
  496.                   _swap_kernel_first_block_paras))
  497.     SWAP_FATAL_ERROR (0);
  498.   _swap_kernel_reclaim_blocks ();
  499.  
  500.   if ((*_swap_kernel_swap_in) (_swap_kernel_handle, _swap_kernel_environment,
  501.                    _swap_kernel_swapped_bytes))
  502.     SWAP_FATAL_ERROR (1);
  503.  
  504.   _asm
  505.     {
  506.       /* get saved stack position */
  507.       mov    ax, cs:_swap_kernel_stack_pointer
  508.       mov    bx, cs:_swap_kernel_stack_segment
  509.  
  510.       /* Interrupts off */
  511.       cli
  512.  
  513.       /* Change stack */
  514.       mov    ss, bx
  515.       mov    sp, ax
  516.  
  517.       /* Interrupts on */
  518.       sti
  519.     }
  520.     /* THE GLOBAL STACK IS SAVE AGAIN! */
  521.  
  522.   return _swap_kernel_return_code;
  523.  
  524. }
  525.  
  526.  
  527.  
  528. /* Display LEN bytes from string MSG and *immediately* return to DOS,
  529.    with CODE as return code.  This is a panic exit, only to be used
  530.    as a last resort.            ~~~~~~~~~~            */
  531.  
  532. void
  533. _swap_kernel_fatal_error (char code, char CODE *msg, size_t len)
  534. {
  535.   _asm
  536.     {
  537.       mov    ax, cs        /* ds = cs */
  538.       mov    ds, ax
  539.       mov    bx, 0x02    /* /dev/stderr */
  540.       mov    dx, offset _swap_kernel_err_msg_head
  541.       mov    cx, length _swap_kernel_err_msg_head
  542.       mov    ah, 0x40    /* MS-DOS Write Handle */
  543.       int    0x21
  544.       mov    dx, msg        /* message */
  545.       mov    cx, len        /* length */
  546.       mov    ah, 0x40
  547.       int    0x21
  548.       mov    al, code    /* bail out */
  549.       mov    ah, 0x4c    /* MS-DOS End Process */
  550.       int    0x21
  551.     }
  552. }
  553.  
  554.  
  555. /* Lowest level disk I/0:  */
  556.  
  557. /* Write SIZE bytes from BUFFER to HANDLE.  Returns 0 on success, -1 on
  558.    failure.  */
  559.  
  560. int
  561. _swap_kernel_write_to_handle (int handle, void _far *buffer, off_t size)
  562. {
  563.   while (size > 0L)
  564.     {
  565.       size_t bytes = (size_t) min (size, FILE_IO_BLKSIZE);
  566.       size_t bytes_written = _swap_kernel_write (handle, buffer, bytes);
  567.       if (bytes_written != bytes)
  568.     return -1;
  569.       FP_ADD (buffer, bytes);
  570.       size -= bytes;
  571.     }
  572.  
  573.   return 0;
  574. }
  575.  
  576. size_t
  577. _swap_kernel_write (int handle, void _far *buffer, size_t bytes)
  578. {
  579.   _asm
  580.     {
  581.       push    ds
  582.       mov    dx, word ptr buffer    /* offset */
  583.       mov    ax, word ptr buffer + 2    /* segment */
  584.       mov    ds, ax
  585.       mov    bx, handle
  586.       mov    cx, bytes
  587.       mov    ah, 0x40        /* MS-DOS Write Handle */
  588.       int    0x21
  589.       jnc    done
  590.       mov    ax, 0xffff
  591.     done:
  592.       pop    ds
  593.     }
  594. }
  595.  
  596.  
  597. /* Read SIZE bytes from HANDLE to BUFFER.  Returns 0 on success, -1 on
  598.    failure.  */
  599.  
  600. int
  601. _swap_kernel_read_from_handle (int handle, void _far *buffer, off_t size)
  602. {
  603.   _swap_kernel_rewind (handle);
  604.  
  605.   while (size > 0L)
  606.     {
  607.       size_t bytes = (size_t) min (size, FILE_IO_BLKSIZE);
  608.       size_t bytes_read = _swap_kernel_read (handle, buffer, bytes);
  609.       if (bytes_read != bytes)
  610.     return -1;
  611.       FP_ADD (buffer, bytes);
  612.       size -= bytes;
  613.     }
  614.  
  615.   return 0;
  616. }
  617.  
  618. size_t
  619. _swap_kernel_read (int handle, void _far *buffer, size_t bytes)
  620. {
  621.   _asm
  622.     {
  623.       push    ds
  624.       mov    dx, word ptr buffer    /* offset */
  625.       mov    ax, word ptr buffer + 2 /* segment */
  626.       mov    ds, ax
  627.       mov    bx, handle
  628.       mov    cx, bytes
  629.       mov    ah, 0x3f        /* MS-DOS Read Handle */
  630.       int    0x21
  631.       jnc    done
  632.       mov    ax, 0xffff
  633.     done:
  634.       pop    ds
  635.     }
  636. }
  637.  
  638.  
  639. /* Rewind the file pointer for HANDLE to the beginning of the file.  */
  640.  
  641. int
  642. _swap_kernel_rewind (int handle)
  643. {
  644.   _asm
  645.     {
  646.       mov    bx, handle
  647.       mov    cx, 0x0000    /* offset = 0 */
  648.       mov    dx, 0x0000
  649.       mov    ax, 0x4200    /* MS-DOS Move File Pointer, (beginning) */
  650.       int    0x21
  651.       jc    failed
  652.       mov    ax, 0x0000
  653.     failed:
  654.     }
  655. }
  656.  
  657. #ifdef USE_XMS
  658.  
  659. /* XMS interface */
  660.  
  661. int
  662. _swap_kernel_xms_move_out (int handle, void _far *buffer, long bytes)
  663. {
  664.   _swap_kernel_xms_move_table.length = bytes;
  665.   _swap_kernel_xms_move_table.src_handle = 0x0000;
  666.   _swap_kernel_xms_move_table.src_offset = (long) buffer;
  667.   _swap_kernel_xms_move_table.dest_handle = handle;
  668.   _swap_kernel_xms_move_table.dest_offset = 0L;
  669.  
  670.   _swap_kernel_xms_move ();
  671. }
  672.  
  673. int
  674. _swap_kernel_xms_move_in (int handle, void _far *buffer, long bytes)
  675. {
  676.   _swap_kernel_xms_move_table.length = bytes;
  677.   _swap_kernel_xms_move_table.dest_handle = 0x0000;
  678.   _swap_kernel_xms_move_table.dest_offset = (long) buffer;
  679.   _swap_kernel_xms_move_table.src_handle = handle;
  680.   _swap_kernel_xms_move_table.src_offset = 0L;
  681.  
  682.   _swap_kernel_xms_move ();
  683. }
  684.  
  685. int
  686. _swap_kernel_xms_move (void)
  687. {
  688.   _asm
  689.     {
  690.       push    ds
  691.       mov    si, offset _swap_kernel_xms_move_table
  692.       mov    ax, seg _swap_kernel_xms_move_table
  693.       mov    ds, ax
  694.       mov    ah, 0x0b
  695.       call    far ptr cs:[_swap_kernel_xms_control]
  696.       cmp    ax, 0x0001
  697.       jne    failed
  698.       mov    ax, 0x0000
  699.       jmp    done
  700.     failed:
  701.       mov    ax, 0xffff
  702.     done:
  703.       pop    ds
  704.     }
  705. }
  706. #endif  /* USE_XMS */
  707.  
  708. #ifdef USE_EMS
  709.  
  710. /* EMS interface */
  711.  
  712. #define PHYSICAL_PAGE    0x00
  713.  
  714. int
  715. _swap_kernel_ems_move_out (int handle, void _far *buffer, long bytes)
  716. {
  717.   void *ptr = buffer;
  718.   int logical_page = 0;
  719.  
  720.   _swap_kernel_ems_save_page_map (handle);
  721.  
  722.   while (bytes > 0L)
  723.     {
  724.       size_t n = (size_t) min (bytes, 0x4000L);
  725.  
  726.       if (_swap_kernel_ems_map_logical_page (handle, logical_page++))
  727.     return -1;
  728.  
  729.       _swap_kernel_memcpy (_swap_kernel_ems_page_frame, ptr, n);
  730.  
  731.       bytes -= (long) n;
  732.       FP_ADD (ptr, n);
  733.     }
  734.  
  735.   _swap_kernel_ems_restore_page_map (handle);
  736.  
  737.   return 0;
  738. }
  739.  
  740.  
  741. int
  742. _swap_kernel_ems_move_in (int handle, void _far *buffer, long bytes)
  743. {
  744.   void *ptr = buffer;
  745.   int logical_page = 0;
  746.  
  747.   _swap_kernel_ems_save_page_map (handle);
  748.  
  749.   while (bytes > 0L)
  750.     {
  751.       size_t n = (size_t) min (bytes, 0x4000L);
  752.  
  753.       if (_swap_kernel_ems_map_logical_page (handle, logical_page++))
  754.     return -1;
  755.  
  756.       _swap_kernel_memcpy (ptr, _swap_kernel_ems_page_frame, n);
  757.  
  758.       bytes -= (long) n;
  759.       FP_ADD (ptr, n);
  760.     }
  761.  
  762.   _swap_kernel_ems_restore_page_map (handle);
  763.  
  764.   return 0;
  765. }
  766.  
  767.  
  768. int
  769. _swap_kernel_ems_map_logical_page (int handle, int logical_page)
  770. {
  771.   _asm
  772.     {
  773.       mov    dx, handle
  774.       mov    bx, logical_page
  775.       mov    ax, 0x4400 + PHYSICAL_PAGE    /* EMS Map Page */
  776.       int    0x67
  777.       mov    cl,  8                /* "mov ax, ah" */
  778.       shr    ax, cl
  779.     }
  780. }
  781.  
  782. int
  783. _swap_kernel_ems_save_page_map (int handle)
  784. {
  785.   _asm
  786.     {
  787.       mov    dx, handle
  788.       mov    ah, 0x47            /* EMS Save Page Map */
  789.       int    0x67
  790.       mov    cl,  8                /* "mov ax, ah" */
  791.       shr    ax, cl
  792.     }
  793. }
  794.  
  795. int
  796. _swap_kernel_ems_restore_page_map (int handle)
  797. {
  798.   _asm
  799.     {
  800.       mov    dx, handle
  801.       mov    ah, 0x48            /* EMS Restore Page Map */
  802.       int    0x67
  803.       mov    cl,  8                /* "mov ax, ah" */
  804.       shr    ax, cl
  805.     }
  806. }
  807. #endif /* USE_EMS */
  808.  
  809. /* Signal handling */
  810.  
  811. /* Simple ^C "handler" that displays a short message and instructs
  812.    MS-DOS to abort the child.  We also set a flag _swap_kernel_USER_INTERRUPT
  813.    which will be used to determine whether such an event occured.
  814.    (This is *very* useful if the child doesn't give a reasonable
  815.    exit code.)
  816.    Note:  resetting the C library signals is NOT enough, since even
  817.    the default handlers (once a non trivial handler has been installed)
  818.    go through the library code.
  819.    Note: make sure that your compiler generates exactly the stackframe we
  820.    undo before the retf.  */
  821.  
  822. void _far
  823. _swap_kernel_int23_handler (void)
  824. {
  825.   /* Record that we've gone through this.  */
  826.   _swap_kernel_user_interrupt = 1;
  827.  
  828.  _asm
  829.     {
  830.       push    ax        /* Carefully save all registers. */
  831.       push    bx
  832.       push    cx
  833.       push    dx
  834.       push    ds
  835.       pushf
  836.  
  837.       sti            /* want to access DOS */
  838.       push    cs        /* ds = cs */
  839.       pop    ds
  840.       mov    bx, 0x02    /* /dev/stderr */
  841.       mov    dx, offset _swap_kernel_int23_message
  842.       mov    cx, length _swap_kernel_int23_message
  843.       mov    ah, 0x40    /* MS-DOS Write Handle */
  844.       int    0x21
  845.  
  846.       popf            /* Carefully restore all registers. */
  847.       pop    ds
  848.       pop    dx
  849.       pop    cx
  850.       pop    bx
  851.       pop    ax
  852.  
  853.       pop    si        /* Undo the C stackframe.  */
  854.       pop    di
  855.       pop    bp
  856.  
  857.       stc            /* The `Carry Flag' tells DOS to kill the    */
  858.       retf            /* child (that's why we don't do an `iret'). */
  859.     }
  860. }
  861.  
  862.  
  863. /* The resident part ends here. */
  864.  
  865. void
  866. _swap_kernel_end (void)
  867. {
  868.   /* NOP */
  869. }
  870.  
  871. /* The transient part starts here. */
  872.  
  873. #pragma check_stack ()
  874.  
  875. /* Install the global parameters.  Execute this as the first function, since
  876.    some macros need _swap_kernel_psp with the correct value.  */
  877.  
  878. void
  879. install_parameters (char *path, char *cmdline, char *env, size_t size)
  880. {
  881.   size_t len = strlen (cmdline);
  882.   struct mcb_info far *mcb;
  883.  
  884.   _fstrcpy ((char _far *) _swap_kernel_path, (char _far *) path);
  885.  
  886.   *_swap_kernel_cmdline = (char) len;
  887.   _fstrcpy ((char _far *) _swap_kernel_cmdline + 1, (char _far *) cmdline);
  888.   _swap_kernel_cmdline[len+1] = '\r';
  889.  
  890.   _swap_kernel_environment_ptr = env;    /* this will be copied later */
  891.   _swap_kernel_environment_size = size;
  892.  
  893.   _swap_kernel_psp = _psp;    /* put them into a save place. */
  894.   _swap_kernel_first_block_paras = FIRST_MCB (mcb)->length;
  895. }
  896.  
  897.  
  898. /* Allocate a swap file named NAME, making sure that at least SIZE bytes
  899.    are available on the disk.  Returns a MS-DOS handle (not to be
  900.    confused with a C file-descriptor!).  */
  901.  
  902. int
  903. alloc_swap_kernel_file (char *name, off_t size)
  904. {
  905.   struct diskfree_t disk_free;
  906.   unsigned drive;
  907.   off_t free;
  908.   int handle;
  909.  
  910.   if (name == NULL || *name == '\0')    /* could create filename ourselves. */
  911.     return -1;
  912.  
  913.   if (name[1] == ':')
  914.     drive = tolower (*name) - 'a' + 1;
  915.   else
  916.     /* Get current drive. */
  917.     _dos_getdrive (&drive);
  918.  
  919.   _dos_getdiskfree (drive, &disk_free);
  920.  
  921.   free = (off_t) disk_free.avail_clusters *
  922.     (off_t) disk_free.sectors_per_cluster * (off_t) disk_free.bytes_per_sector;
  923.  
  924.   if (free < size)
  925.     return (-1);
  926.  
  927.   if (_dos_creat (name, _A_NORMAL, &handle))
  928.     return (-1);
  929.   else
  930.     return handle;
  931. }
  932.  
  933. /* Close and delete the temporary file.  */
  934.  
  935. unsigned int
  936. cleanup_swap_kernel_file (unsigned int handle, char *name)
  937. {
  938.   return !_dos_close (handle) && !unlink (name);
  939. }
  940.  
  941.  
  942. #ifdef USE_XMS
  943.  
  944. /* More XMS */
  945. /* Microsoft's recommendation:  */
  946.  
  947. int
  948. xms_installed (void)
  949. {
  950.   _asm
  951.     {
  952.       mov    ax, 0x4300
  953.       int    0x2f
  954.       cmp    al, 0x80
  955.       jne    failed
  956.       mov    ax, 0x0001
  957.       jmp    done
  958.     failed:
  959.       mov    ax, 0x0000
  960.     done:
  961.     }
  962. }
  963.  
  964. void
  965. xms_get_control_function (void)
  966. {
  967.   _asm
  968.     {
  969.       mov    ax, 0x4310
  970.       int    0x2f
  971.       mov    word ptr cs:_swap_kernel_xms_control, bx
  972.       mov    bx, es
  973.       mov    word ptr cs:_swap_kernel_xms_control + 2, bx
  974.     }
  975. }
  976.  
  977. unsigned int
  978. xms_allocate_memory (unsigned int kilobytes)
  979. {
  980.   _asm
  981.     {
  982.       mov    dx, kilobytes
  983.       mov    ah, 0x09
  984.       call    far ptr cs:[_swap_kernel_xms_control]
  985.       cmp    ax, 0x0001
  986.       jne    failed
  987.       mov    ax, dx
  988.       jmp    done
  989.     failed:
  990.       mov    ax, 0xffff
  991.     done:
  992.     }
  993. }
  994.  
  995. unsigned int
  996. xms_free_memory (unsigned int handle)
  997. {
  998.   _asm
  999.     {
  1000.       mov    dx, handle
  1001.       mov    ah, 0x0a
  1002.       call    far ptr cs:[_swap_kernel_xms_control]
  1003.       cmp    ax, 0x0001
  1004.       je    done
  1005.       mov    ax, 0x0000
  1006.     done:
  1007.     }
  1008. }
  1009. #endif /* USE_XMS */
  1010.  
  1011. #ifdef USE_EMS
  1012.  
  1013. /* More EMS */
  1014.  
  1015. /* Test for presence of LIM EMS 4.0.
  1016.    (this procedure is taken from the LIM specification).  */
  1017.  
  1018. int
  1019. ems_present (void)
  1020. {
  1021. #ifndef BROKEN_EMM
  1022.   static char _far ems_id[] = "EMMXXXX0"; /* LIM EMS 4.0 identification. */
  1023. #else
  1024.   /* This is for debugging only, don't worry about it ... */
  1025.   static char _far ems_id[] = "Junk-EMM";
  1026. #endif
  1027.  
  1028.   char _far *ems_device = (char _far *) _dos_getvect (0x67);
  1029.  
  1030.   FP_OFF (ems_device) = 0x000a;
  1031.  
  1032.   return !_fstrncmp (ems_id, ems_device, sizeof (ems_id) - 1);
  1033. }
  1034.  
  1035. /* Allocate pages from the EMS Manager.  Returns handle or -1 no error.  */
  1036.  
  1037. int
  1038. ems_alloc_pages (int n)
  1039. {
  1040.   _asm
  1041.     {
  1042.       mov    bx, n
  1043.       mov    ah, 0x43    /* EMS Allocate Pages */
  1044.       int    0x67
  1045.       cmp    ah, 0x00
  1046.       jz    success
  1047.       mov    ax, 0xffff    /* failure */
  1048.       ret
  1049.     success:
  1050.       mov    ax, dx        /* return handle */
  1051.     }
  1052. }
  1053.  
  1054. /* Free pages allocated for HANDLE.  Returns 0 if successful.  */
  1055.  
  1056. int
  1057. ems_free_pages (unsigned int handle)
  1058. {
  1059.   _asm
  1060.     {
  1061.       mov    dx, handle
  1062.       mov    ah, 0x45    /* EMS Free Pages */
  1063.       int    0x67
  1064.       mov    cl, 8        /* "mov ax, ah" */
  1065.       shr    ax, cl
  1066.     }
  1067. }
  1068.  
  1069. /* Return far pointer to EMS page frame.  */
  1070.  
  1071. void _far *
  1072. ems_get_page_frame (void)
  1073. {
  1074.   void _far *frame = (void _far *) 0;
  1075.  
  1076.   _asm
  1077.     {
  1078.       mov    ah, 0x41        /* EMS Page Frame */
  1079.       int    0x67
  1080.       cmp    ah, 0x00
  1081.       jz    success
  1082.       ret                /* failure */
  1083.     success:
  1084.       mov    word ptr frame + 2, bx    /* segment of page frame */
  1085.     }
  1086.  
  1087.   return frame;
  1088. }
  1089.  
  1090. #endif /* USE_EMS */
  1091.  
  1092. /* Return the last MCB owned by us.
  1093.    WARNING:  This assumes that _swap_kernel_psp has already been set to _PSP
  1094.          (e.g. by install_parameters())   */
  1095.  
  1096. struct mcb_info far *
  1097. last_mcb (void)
  1098. {
  1099.   struct mcb_info far *mcb;
  1100.   struct mcb_info far *ret;
  1101.  
  1102.   FIRST_MCB (mcb);
  1103.  
  1104.   while (mcb->id_byte == 'M')
  1105.     {
  1106.       if (OUR_MCB (mcb))
  1107.     ret = NEXT_MCB (mcb);
  1108.       else
  1109.     NEXT_MCB (mcb);
  1110.     }
  1111.  
  1112.   if (mcb->id_byte == 'Z')    /* found the end */
  1113.     return ret;
  1114.   else                /* error */
  1115.     return NULL;
  1116. }
  1117.  
  1118.  
  1119. /* MODE is the preferred swapping mode, if XMS or EMS are requested but not
  1120.    available, it is mapped to DISK.  PATH is the complete path of the program
  1121.    to be executed, it must not be longer than MAX_MSDOS_PATH (=144).  CMDLINE
  1122.    is the commandline to be passed to the program, it must not be longer than
  1123.    MAX_MSDOS_CMDLINE (=126).  ENV is a well formed MS-DOS environment of
  1124.    length LEN, including the terminating '\0's.  FILE is a valid filename,
  1125.    which will be used for a possible disk swap file.  */
  1126.  
  1127. int
  1128. _swap_spawn_child (enum swapping_mode mode, char *path, char *cmdline,
  1129.            char *env, int len, char *file)
  1130. {
  1131.   int rc;
  1132.   unsigned int (*cleanup_function) (unsigned int handle,...);
  1133.  
  1134.   install_parameters (path, cmdline, env, len);
  1135.  
  1136.   _swap_kernel_environment = (char _far *) _swap_kernel_end;
  1137.   FP_PARA_ALIGN (_swap_kernel_environment);
  1138.  
  1139.   _swap_kernel_swapped_bytes = (long) ((char _huge *) last_mcb ()
  1140.             - (char _huge *) _swap_kernel_environment);
  1141.  
  1142.   switch (mode)
  1143.     {
  1144.     case xms:
  1145. #ifdef USE_XMS
  1146.       if (xms_installed ())
  1147.     {
  1148.       xms_get_control_function ();
  1149.       _swap_kernel_swap_out = _swap_kernel_xms_move_out;
  1150.       _swap_kernel_swap_in = _swap_kernel_xms_move_in;
  1151.       cleanup_function = xms_free_memory;
  1152.       _swap_kernel_handle
  1153.         = xms_allocate_memory ((unsigned int)
  1154.                    ((_swap_kernel_swapped_bytes - 1) >> 10)
  1155.                    + 1);
  1156.       if (_swap_kernel_handle != -1)
  1157.         break;
  1158.     }
  1159. #endif /* USE_XMS */
  1160.       /* fall through */
  1161.  
  1162.     case ems:
  1163. #ifdef USE_EMS
  1164.       if (ems_present ())
  1165.     {
  1166.       _swap_kernel_swap_out = _swap_kernel_ems_move_out;
  1167.       _swap_kernel_swap_in = _swap_kernel_ems_move_in;
  1168.       cleanup_function = ems_free_pages;
  1169.       _swap_kernel_ems_page_frame = ems_get_page_frame ();
  1170.       _swap_kernel_handle
  1171.         = ems_alloc_pages ((unsigned int)
  1172.                    ((_swap_kernel_swapped_bytes - 1) >> 14) + 1);
  1173.       if (_swap_kernel_ems_page_frame && _swap_kernel_handle != -1)
  1174.         break;
  1175.     }
  1176. #endif /* USE_EMS */
  1177.       /* fall through */
  1178.  
  1179.     case disk:
  1180.       _swap_kernel_swap_out = _swap_kernel_write_to_handle;
  1181.       _swap_kernel_swap_in = _swap_kernel_read_from_handle;
  1182.       cleanup_function = cleanup_swap_kernel_file;
  1183.       _swap_kernel_handle
  1184.     = alloc_swap_kernel_file (file, _swap_kernel_swapped_bytes);
  1185.       if (_swap_kernel_handle != -1)
  1186.     break;
  1187.  
  1188.       errno = ENOSPC;
  1189.       return -1;
  1190.  
  1191.     case none:
  1192.  
  1193.       /* FIX ME  (i.e. code me) */
  1194.       errno = EINVAL;
  1195.       return -1;
  1196.     }
  1197.  
  1198.   _swap_kernel_user_interrupt = 0;
  1199.  
  1200.   /* temporarily disable ^C  */
  1201.   _swap_kernel_caller_int23 = _dos_getvect (0x23);
  1202.   _dos_setvect (0x23, (signal_handler *) _swap_kernel_int23_handler);
  1203.  
  1204.   rc = _swap_kernel_spawn_child ();
  1205.  
  1206.   /* did the user hit ^C ? */
  1207.   if (_swap_kernel_user_interrupt)
  1208.     rc |= (SIGINT << 8);
  1209.  
  1210.   _dos_setvect (0x23, _swap_kernel_caller_int23);
  1211.  
  1212.   cleanup_function (_swap_kernel_handle, file);
  1213.  
  1214.   return rc;
  1215. }
  1216.  
  1217.  
  1218. /* 
  1219.  * Local Variables:
  1220.  * mode:C
  1221.  * minor-mode:auto-fill
  1222.  * ChangeLog:ChangeLog
  1223.  * compile-command:make
  1224.  * End:
  1225.  */
  1226.